﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace Krile.Kernel.Control.Surface
{
    public abstract class CommandButton : IMouseControllable
    {
        public Krile.Kernel.Bridges.IForm ParentForm { get; set; }

        public bool HitTest(Point position)
        {
            return inTarget.HitTest(position);
        }

        Rectangle inTarget = new Rectangle();
        /// <summary>
        /// コマンドボタンの描画
        /// </summary>
        /// <remarks>
        /// CommandButtonRendererに依存します。
        /// </remarks>
        public void Draw(Graphics g, Point ltp)
        {
            Rectangle outer = new Rectangle(ltp, new Size(CommandButtonRenderer.ButtonSize, CommandButtonRenderer.ButtonSize));
            inTarget = outer;
            if (this.Checked)
            {
                using (var b = new SolidBrush(Color.FromArgb(180, Color.SkyBlue)))
                {
                    g.FillRectangle(b, outer);
                }
                outer.Width--;
                outer.Height--;
                g.DrawRectangle(Pens.RoyalBlue, outer);
                outer.Width++;
                outer.Height++;
            }
            if (this.pointed)
            {
                using (var b = new SolidBrush(Color.CornflowerBlue))
                {
                    g.FillRectangle(b, outer);
                }
                outer.Width--;
                outer.Height--;
                g.DrawRectangle(Pens.RoyalBlue, outer);
                outer.Width++;
                outer.Height++;
            }
            if (IconImage == null) return;
            Rectangle inner = new Rectangle(ltp, new Size(CommandButtonRenderer.ButtonInnerSize, CommandButtonRenderer.ButtonInnerSize));
            int gap = (CommandButtonRenderer.ButtonSize - CommandButtonRenderer.ButtonInnerSize) / 2;
            inner.X += gap;
            inner.Y += gap;
            if (Enabled)
                g.DrawImage(IconImage, inner);
            else
            {
                using (var i = Images.TransformGrayscale(IconImage))
                    g.DrawImage(i, inner);
            }
        }

        public abstract string Description { get; }

        public string HelptipText
        {
            get { return Description; }
        }

        public virtual bool Enabled { get; set; }

        public virtual bool Checked { get; set; }

        private bool pointed = false;
        public virtual bool Pointed
        {
            get { return pointed; }
            set
            {
                pointed = value;
                if (ParentForm != null)
                    ParentForm.RefreshTL(false);
            }
        }

        public virtual Image IconImage { get; set; }

        #region Virtual methods

        public virtual void OnMouseOver(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public virtual void OnMouseMove(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public virtual void OnMouseEnter(object sender, ChainMouseEventArgs e)
        {
            Pointed = true;
            if (ParentForm != null)
                ParentForm.SetCursor(Cursors.Hand);
            e.SetThrowSub();
        }

        public virtual void OnMouseLeave(object sender, ChainMouseEventArgs e)
        {
            Pointed = false;
            if (ParentForm != null)
                ParentForm.SetCursor(Cursors.Default);
            e.SetThrowSub();
        }

        public virtual void OnMouseClick(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public virtual void OnMouseDoubleClick(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public virtual void OnMouseDown(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public virtual void OnMouseUp(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        #endregion

    }

    public class CommandButtonDrop : IMouseControllable
    {
        public Krile.Kernel.Bridges.IForm ParentForm { get; set; }

        private CommandButton[] overflow = null;
        public void SetOverflowButtons(CommandButton[] ovfs)
        {
            overflow = ovfs;
        }

        public bool HitTest(Point position)
        {
            return inTarget.HitTest(position);
        }

        Rectangle inTarget = new Rectangle();
        public void Draw(Graphics g, Point ltp)
        {
            Rectangle outer = new Rectangle(ltp, new Size(CommandButtonRenderer.DropWidth, CommandButtonRenderer.ButtonSize));
            inTarget = outer;
            if(this.Pointed)
            {
                using (var b = new SolidBrush(Color.CornflowerBlue))
                {
                    g.FillRectangle(b, outer);
                }
                outer.Width--;
                outer.Height--;
                g.DrawRectangle(Pens.RoyalBlue, outer);
                outer.Width++;
                outer.Height++;
            }
            Rectangle inner = new Rectangle(ltp, new Size(CommandButtonRenderer.DropInnerWidth, CommandButtonRenderer.ButtonInnerSize));
            int gap = (CommandButtonRenderer.ButtonSize - CommandButtonRenderer.ButtonInnerSize) / 2;
            inner.Y += gap;
            gap = (CommandButtonRenderer.DropWidth - CommandButtonRenderer.DropInnerWidth) / 2;
            inner.X += gap;
            g.DrawImage(Properties.Resources.downarrow , inner);
        }

        public string Description { get { return "隠れたボタンを表示..."; } }

        private bool _pointed;
        public bool Pointed
        {
            get { return _pointed; }
            set
            {
                _pointed = value;
                if (ParentForm != null)
                    ParentForm.RefreshTL(false);
            }
        }

        public string HelptipText
        {
            get { return Description; }
        }

        public void OnMouseOver(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public void OnMouseMove(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public void OnMouseEnter(object sender, ChainMouseEventArgs e)
        {
            Pointed = true;
            if (ParentForm != null)
            {
                ParentForm.SetCursor(Cursors.Hand);
                ParentForm.RefreshTL(false);
            }
        }

        public void OnMouseLeave(object sender, ChainMouseEventArgs e)
        {
            Pointed = false;
            if (ParentForm != null)
            {
                ParentForm.SetCursor(Cursors.Default);
                ParentForm.RefreshTL(false);
            }
        }

        private ContextMenuStrip context = null;
        public void OnMouseClick(object sender, ChainMouseEventArgs e)
        {
            if (context != null)
                context.Dispose();
            if (overflow == null)
                return;
            context = new ContextMenuStrip();
            foreach (var ovf in overflow)
            {
                var ci = ovf;
                ToolStripMenuItem tsmi = new ToolStripMenuItem(
                    ovf.Description, ovf.IconImage,
                    new EventHandler((o, ev) => ci.OnMouseClick(o, e)));
                tsmi.Checked = ovf.Checked;
                tsmi.Enabled = ovf.Enabled;
                context.Items.Add(tsmi);
            }
            context.Show(((System.Windows.Forms.Control)sender).PointToScreen(new Point(inTarget.X, inTarget.Bottom)));
        }

        public void OnMouseDoubleClick(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public void OnMouseDown(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }

        public void OnMouseUp(object sender, ChainMouseEventArgs e)
        {
            e.SetThrowSub();
        }
    }

    public static class CommandButtonRenderer
    {
        //outer size
        public const int ButtonSize = 20;
        //inner size
        public const int ButtonInnerSize = 16;
        //dropper width
        public const int DropWidth = 10;
        //dropper inner
        public const int DropInnerWidth = 6;

        static CommandButtonDrop cbdrop = new CommandButtonDrop();
        /// <summary>
        /// コマンドボタン配列の描画を行います。
        /// </summary>
        /// <param name="buttons">コマンドボタン配列</param>
        /// <param name="g">グラフィックス</param>
        /// <param name="leftTop">左上座標</param>
        /// <param name="capableWidth">描画可能な幅</param>
        /// <returns>
        /// 描画しきれなかった場合、CommandButtonDropが戻ります。
        /// <para>マウスイベント処理チェーンに参加させてください。</para>
        /// </returns>
        public static CommandButtonDrop Draw(this CommandButton[] buttons, Graphics g, Point leftTop, int capableWidth, out int renderedWidth)
        {
            //描画した長さをセット(しないと怒られるので)
            renderedWidth = 0;
            //描画するものがなければ戻る
            if (buttons == null || buttons.Length == 0)
                return null;

            //描画最大長を計算
            int mxWidth = buttons.Length * ButtonSize;
            if (capableWidth >= mxWidth)
            {
                //全部描画できそう
                renderedWidth = mxWidth;
                leftTop.X += capableWidth - mxWidth;
                foreach (var b in buttons)
                {
                    b.Draw(g, leftTop);
                    leftTop.X += ButtonSize;
                }
                return null;
            }
            else
            {
                capableWidth -= 10;
                if (capableWidth > 0)
                    leftTop.X += capableWidth % ButtonSize;
                int btnCtor = 0;
                while(capableWidth > ButtonSize)
                {
                    buttons[btnCtor].Draw(g, leftTop);
                    capableWidth -= ButtonSize;
                    leftTop.X += ButtonSize;
                    btnCtor++;
                }
                renderedWidth = btnCtor * ButtonSize + 10;
                CommandButton[] ovfs = new CommandButton[buttons.Length - btnCtor];
                for (int i = btnCtor; i < buttons.Length; i++)
                {
                    ovfs[i - btnCtor] = buttons[i];
                }
                cbdrop.SetOverflowButtons(ovfs);
                cbdrop.Draw(g, leftTop);
                return cbdrop;
            }
        }
    }
}
